package com.hexb.smh.controller.client;

import com.hexb.core.common.Page;
import com.hexb.core.utils.Assert;
import com.hexb.core.utils.ObjectUtils;
import com.hexb.smh.annotations.AopLoginLog;
import com.hexb.smh.annotations.AopOperationLog;
import com.hexb.smh.annotations.SecretIn;
import com.hexb.smh.entity.Account;
import com.hexb.smh.entity.Brand;
import com.hexb.smh.entity.Config;
import com.hexb.smh.entity.enums.ConfigType;
import com.hexb.smh.entity.enums.LoginType;
import com.hexb.smh.entity.enums.Operators;
import com.hexb.smh.entity.enums.RoleType;
import com.hexb.smh.entity.param.account.ActivePasswordParam;
import com.hexb.smh.entity.param.account.ClientLoginParam;
import com.hexb.smh.entity.param.account.PasswordModifierParam;
import com.hexb.smh.entity.param.config.EvaluateParam;
import com.hexb.smh.entity.shiro.SmhUsernamePasswordToken;
import com.hexb.smh.entity.view.ClientTokenAccount;
import com.hexb.smh.entity.view.EvaluateResult;
import com.hexb.smh.entity.view.IndexedProductView;
import com.hexb.smh.entity.view.ProductClientView;
import com.hexb.smh.errors.ErrorCodes;
import com.hexb.smh.mapper.BrandMapper;
import com.hexb.smh.service.IAccountService;
import com.hexb.smh.service.IConfigService;
import com.hexb.smh.service.IEvaluateService;
import com.hexb.smh.service.IProductService;
import com.hexb.smh.utils.AccountHelper;
import com.hexb.smh.utils.HttpUtils;
import com.hexb.smh.utils.OperationConst;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresAuthentication;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;

import java.io.IOException;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.*;

import static com.hexb.smh.utils.SmhConstant.*;

/**
 * @author : hexb
 */
@RestController
@RequestMapping("/client")
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
@Api(value = "客户端", description = "Client", tags = {"C1*.客户端"})
@Slf4j
public class ClientController {

    static final private List<String> LETTERS = new ArrayList<>();

    static final SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");

    static {
        for (char c = 'A'; c <= 'Z'; c++) {
            LETTERS.add(String.valueOf(c));
        }
    }

    @NonNull
    final private IAccountService accountService;

    @NonNull
    final private IProductService productService;

    @NonNull
    final private IEvaluateService evaluateService;

    @NonNull
    final private IConfigService configService;

    @AopLoginLog(type = LoginType.login)
    @ApiOperation(value = "业务员登录")
    @PostMapping("/login")
    @SecretIn
    public ClientTokenAccount salesmanLogin(@RequestBody @Valid ClientLoginParam param, HttpServletRequest request) {
        Subject subject = SecurityUtils.getSubject();
        SmhUsernamePasswordToken upt = new SmhUsernamePasswordToken(param.getLoginName(), param.getRawPassword());
        upt.setRole(RoleType.salesman);
        subject.login(upt);
        String token = (String) subject.getSession().getId();
        String ip = HttpUtils.getRequestIp(request);
        Account account = AccountHelper.getAccount();
        account.setLastLoginIp(ip);
        accountService.loginLog(account.getId(), ip);
        ClientTokenAccount clientTokenAccount = new ClientTokenAccount();
        BeanUtils.copyProperties(account, clientTokenAccount);
        clientTokenAccount.setToken(token);
        return clientTokenAccount;
    }


    @AopLoginLog(type = LoginType.logout)
    @ApiOperation(value = "退出登录")
    @PostMapping("/logout")
    @RequiresAuthentication
    public void salesmanLogout() {
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
    }

    /**
     * 首次登录时,重设密码.
     * 该接口每个用户只成功调用一次
     */
    @ApiOperation(value = "业务员首次重设密码")
    @PutMapping("/active")
    @RequiresAuthentication
    @RequiresRoles(value = {ROLE_SALESMAN, ROLE_UN_ACTIVATED}, logical = Logical.AND)
    @AopOperationLog(name = "首次重设密码", operations = Operators.activeAccount)
    public int activeAccount(@RequestBody @Valid ActivePasswordParam param) {
        Boolean activated = AccountHelper.getAccount().getActivated();
        Assert.isTrue(activated, ErrorCodes.ACCOUNT_ALREADY_ACTIVATED);
        return accountService.active(param.getRawPassword());
    }


    @ApiOperation(value = "业务员修改自己密码")
    @PutMapping("/changePassword")
    @RequiresAuthentication
    @AopOperationLog(name = OperationConst.CHANGE_PASSWORD)
    public int changePassword(@RequestBody @Valid PasswordModifierParam param) {
        int i = accountService.changePassword(param.getOldPassword(), param.getNewPassword());
        SecurityUtils.getSubject().logout();
        return i;
    }

    @RequiresRoles(value = {ROLE_SALESMAN, ROLE_ACTIVATED}, logical = Logical.AND)
    @ApiOperation(value = "*查询可用项目(分页)")
    @GetMapping("/page/{pageSize}/{pageNum}")
    public Page<ProductClientView> page(@ApiParam("搜索关键字") String searchKey,
                                        @ApiParam("*品牌Id") Integer brandId,
                                        @PathVariable int pageSize,
                                        @PathVariable int pageNum) {
        return productService.searchClient(brandId == null ? Brand.DEFAULT : brandId, searchKey, pageSize, pageNum);
    }


    @ApiOperation(value = "*查询可用项目(全部)")
    @GetMapping("/list")
    @RequiresRoles(value = {ROLE_SALESMAN, ROLE_ACTIVATED}, logical = Logical.AND)
    public List<ProductClientView> list(Integer brandId, String searchKey) {
        return productService.searchClient(brandId == null ? Brand.DEFAULT : brandId, searchKey);
    }


    /**
     * tag:2020.10.21修改为按分类分组
     */
    @RequiresRoles(value = {ROLE_SALESMAN, ROLE_ACTIVATED}, logical = Logical.AND)
    @ApiOperation("*获取全部项目(按分类)")
    @GetMapping("/indexed")
    public List<IndexedProductView> indexed(Integer brandId, String searchKey) {
        List<ProductClientView> list = productService.searchClient(brandId == null ? Brand.DEFAULT : brandId, searchKey);
        Map<String, List<ProductClientView>> group = ObjectUtils.listGroup(list, ProductClientView::getCategory);
        List<IndexedProductView> indexedList = new ArrayList<>();

        Set<String> categories = group.keySet();
        int[] indexes = new int[]{1};
        categories.forEach(c -> {
            IndexedProductView indexed = new IndexedProductView();
            indexed.setItems(group.getOrDefault(c, Collections.emptyList()));
            indexed.setOrder(indexes[0]++);
            indexed.setTitle(c);
            indexed.setKey(StringUtils.isEmpty(c) ? "未分" : c.substring(0, 2));
            indexedList.add(indexed);
        });
        return indexedList;
    }


    @RequiresRoles(value = {ROLE_SALESMAN, ROLE_ACTIVATED}, logical = Logical.AND)
    @ApiOperation(value = "套餐评分")
    @AopOperationLog(name = "提交套餐评估(业务端)", operations = Operators.evaluate)
    @PostMapping("/evaluate")
    public EvaluateResult evaluate(@RequestBody @Valid EvaluateParam param) {
        return evaluateService.clientEvaluate(
                param.getIds(),
                param.getDealPrice(),
                param.getBrandId() == null ? Brand.DEFAULT : param.getBrandId()
        );
    }

    @RequiresRoles(value = {ROLE_SALESMAN, ROLE_ACTIVATED}, logical = Logical.AND)
    @ApiOperation(value = "下载评估结果")
    @GetMapping("/download/{id}")
    public void download(@PathVariable Integer id, HttpServletResponse response) {
        ServletOutputStream os = null;
        try {
            response.setCharacterEncoding("utf-8");
            response.setContentType("application/vnd.ms-excel");
            response.addHeader("Content-Disposition", "attachment;fileName="
                    + URLEncoder.encode(String.format("评估结果-%s.xls", format.format(new Date())), "utf-8"));
            os = response.getOutputStream();
            evaluateService.exportEvaluateHistory(id, os);
        } catch (IOException e) {
            log.error("获取输出流发生错误");
        } finally {
            if (os != null) {
                try {
                    os.close();
                } catch (IOException e) {
                    log.error("导出评估历史发生错误", e);
                }
            }
        }
    }

    @NonNull
    final private BrandMapper brandMapper;

    @RequiresRoles(value = {ROLE_SALESMAN, ROLE_ACTIVATED}, logical = Logical.AND)
    @ApiOperation("获取品牌列表")
    @GetMapping("/brand")
    public List<Brand> list() {
        return brandMapper.list();
    }

    @RequiresRoles(value = {ROLE_SALESMAN, ROLE_ACTIVATED}, logical = Logical.AND)
    @ApiOperation("获取系统公告内容")
    @GetMapping("/announcement")
    public String announcement() {
        Map<ConfigType, Config> map = configService.map();
        return map.getOrDefault(ConfigType.announcement, new Config()).getValue();
    }

}
